/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package z.h.w.jar.data.tree;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import z.h.w.jar.kit.method.To;
import z.h.w.jar.kit.ex.CanNotFindException;

/**
 *
 * @author EnzoZhong
 * @param <T>
 */
public class ReferTree<T> implements Tree<T> {

      private static final long serialVersionUID = 1L;
      final private String BLANK_STRING = "             ";
      final private String ENTER = "\r\n";
      /*
       * 树节点
       */
      private ReferTree<T> up;
      private T val;
      private Set<ReferTree<T>> son;
      /*
       * 树运算
       */
      private Stack<ReferTree<T>> stack;
      /*
       * 树维护
       */
      private StringBuffer sb;
      //每一次CRUD操作维护该链表
      private List<Map<Object , Object>> mapList;
      //计算id数
      final private String pid = "pId";
      final private String id = "id";
      final private String name = "name";
      private Integer count = -1;
      private Map<Object , Object> ROOT;

      private ReferTree () {
            this.stack = new Stack<> ();
            this.son = new LinkedHashSet<> ( 11 );
            this.mapList = new LinkedList<> ();
            this.val = null;
      }

      /**
       * 初始化树的root，并赋值为val
       *
       * @param val
       */
      public ReferTree ( T val ) {
            /*
             * 引用树自身维护
             */
            this ();
            this.val = val;
            /*
             * 引用节点链表维护
             */
            ROOT = new HashMap<> ( 11 );
            if ( val instanceof java.util.Map ) {
                  @SuppressWarnings ( "unchecked" )
                  Map<Object , Object> temp = ( Map<Object , Object> ) val;
                  ROOT.putAll ( temp );
            } else {
                  ROOT.put ( name , val );
            }
            ROOT.put ( pid , getID () );
            ROOT.put ( id , getID () );
            mapList.add ( ROOT );
      }

      private ReferTree ( ReferTree<T> up , T val ) {
            this ( val );
            this.up = up;
      }

      private Integer getID () {
            return count ++;
      }

      /*
       * 一次向相同层级的目录插入多个元素
       */
      /**
       *
       * @param vals
       * @param path
       *
       * @return
       */
      public List<ReferTree<T>> add ( T[] vals , T... path ) {
            List<ReferTree<T>> re = new LinkedList<> ();
            for ( T t : vals ) {
                  re.add ( this.add ( t , path ) );
            }
            return re;
      }

      @Override
      public ReferTree<T> add ( T val , T... path ) {
            /*
             * 引用树自身维护
             */
            ReferTree<T> root = get ( path );
            ReferTree<T> newNode = new ReferTree<> ( root , val );
            root.getSon ().add ( newNode );
            /*
             * 引用节点链表维护
             */
            Map<Object , Object> map = new HashMap<> ( 88 );
            if ( val instanceof java.util.Map ) {
                  Map<Object , Object> temp = ( Map<Object , Object> ) val;
                  map.putAll ( temp );
            } else {
                  map.put ( name , val );
            }
            //如果该树有上部
            if ( newNode.hasUp () ) {
                  //寻找上部已储存的ID
                  Integer upID = getUpID ( newNode , path );
                  map.put ( pid , upID );
                  map.put ( id , getID () );
            } else {
                  //没有上部
                  map.put ( pid , getID () );
                  map.put ( id , getID () );
            }
            //设置好的node加入列表中
            mapList.add ( map );
            return newNode;
      }

      /**
       * 寻找已存储的中的所有listNode的父值是否匹配所输入的tree 。
       * <p/>
       * 这个方法可以确定所要寻找的tree的父值是否已经有了现成的id
       */
      private Integer getUpID ( Tree<T> newNode , T... path ) {
            Iterator<Map<Object , Object>> iterator = mapList.iterator ();
            while ( iterator.hasNext () ) {
                  Map<Object , Object> listNode = iterator.next ();
                  //如果list中的listNode中有匹配tree的父属性
                  if ( val instanceof java.util.Map ) {
                        Map<Object , Object> tmp = ( Map<Object , Object> ) newNode.getUp ().getVal ();
                        if ( listNode.get ( name ).equals ( tmp.get ( name ) ) ) {
                              return ( Integer ) listNode.get ( id );
                        }
                  } else if ( listNode.get ( name ).equals ( newNode.getUp ().getVal () ) ) {
                        return ( Integer ) listNode.get ( id );
                  }
            }
            return 0;
      }

      /**
       *
       * @param array
       *
       * @return
       *
       * @throws CanNotFindException
       */
      @Override
      public ReferTree<T> get ( T... array ) throws CanNotFindException {
            try {
                  ReferTree<T> tmp = this;
                  for ( T type : array ) {
                        tmp = get ( type , tmp );
                  }
                  return tmp;
            } catch ( CanNotFindException ex ) {
                  throw new CanNotFindException ( "can`t find this val " + To.array2String ( array ) );
            }
      }

      private ReferTree<T> get ( T val , ReferTree<T> tree ) throws CanNotFindException {
            //判断该是否该树
            if ( tree.hasVal ( val ) ) {
                  return tree;
            }
            //判断是否该树下的分支
            Set<ReferTree<T>> sons = tree.getSon ();
            for ( Iterator<ReferTree<T>> iterator = sons.iterator () ; iterator.hasNext () ; ) {
                  ReferTree<T> tmp = iterator.next ();
                  if ( tmp.hasVal ( val ) ) {
                        return tmp;
                  }
            }
            //递归所有分支
            if ( tree.hasSon () ) {
                  Set<ReferTree<T>> list = tree.getSon ();
                  for ( Iterator<ReferTree<T>> iterator = list.iterator () ; iterator.hasNext () ; ) {
                        ReferTree<T> tmp = iterator.next ();
                        stack.push ( tmp );
                  }
                  return get ( val , stack.pop () );
            } else if (  ! stack.isEmpty () ) {
                  return get ( val , stack.pop () );
            }
            throw new CanNotFindException ( "can`t find this val " + val );
      }

      /**
       *
       * @param node
       *
       * @return
       */
      public Boolean del ( T node ) {
            try {
                  ReferTree<T> target = get ( node , this );
                  ReferTree<T> upNode = target.getUp ();
                  upNode.getSon ().remove ( target );
                  return true;
            } catch ( Exception ex ) {
                  return false;
            }
      }

      /**
       *
       * @param path
       *
       * @return
       */
      @Override
      public Boolean del ( T... path ) {
            throw new UnsupportedOperationException ( "Not supported yet." );
      }

      /**
       *
       * @return
       */
      @Override
      public ReferTree<T> getUp () {
            return up;
      }

      /**
       *
       * @return
       */
      @Override
      public T getVal () {
            return this.val;
      }

      /**
       *
       * @return
       */
      @Override
      public Boolean hasUp () {
            return this.up != null;
      }

      /**
       *
       * @return
       */
      public Boolean hasSon () {
            return this.son.size () > 0;
      }

      /**
       *
       * @param val
       * <p/>
       * @return
       */
      public Boolean hasVal ( T val ) {
            return this.val.equals ( val );
      }

      /**
       *
       * @return
       */
      public Set<ReferTree<T>> getSon () {
            return this.son;
      }

      /**
       *
       * @param node
       *
       * @return
       */
      public List<T> getValList ( T node ) {
            try {
                  ReferTree<T> tmp = get ( node , this );
                  Set<ReferTree<T>> list = tmp.getSon ();
                  List<T> re = new LinkedList<> ();
                  for ( ReferTree<T> tree : list ) {
                        re.add ( tree.getVal () );
                  }
                  return re;
            } catch ( Exception ex ) {
                  return null;
            }

      }

      /**
       *
       * @param node
       *
       * @return
       */
      @Override
      public Boolean has ( T... node ) {
            try {
                  this.get ( node );
                  return true;
            } catch ( Exception ex ) {
                  return false;
            }
      }

      private Integer getDeep ( ReferTree tree ) {
            int re = 1;
            if ( tree.getUp () != null ) {
                  while (  ! tree.getUp ().hasVal ( this.val ) ) {
                        re ++;
                  }
            }
            return re;
      }

      private String toStr ( Integer times , String type ) {
            StringBuffer tmp = new StringBuffer ();
            for ( int x = 0 ; x < times ; x ++ ) {
                  tmp.append ( type );
            }
            return tmp.toString ();
      }

      private String toStr () {
            sb.append ( this.getVal () ).append ( ENTER );
            Set<ReferTree<T>> list = getSon ();
            for ( ReferTree<T> tree : list ) {
                  toStr ( tree , getDeep ( tree ) );
            }
            String re = sb.toString ();
            sb = new StringBuffer ();
            return re;
      }

      private void toStr ( ReferTree tree , int tabNum ) {
            for ( int i = 0 ; i < tabNum ; i ++ ) {
                  sb.append ( toStr ( 1 , this.BLANK_STRING ) );
            }
            sb.append ( tree.getVal () ).append ( ENTER );
            Set<ReferTree> list = tree.getSon ();
            for ( ReferTree tmp : list ) {
                  toStr ( tmp , tabNum + 1 );
            }

      }

      @Override
      public String toString () {
            this.sb = new StringBuffer ();
            return toStr ();
      }

      /**
       *
       * @param includeSelf
       *
       * @return
       */
      public List<Tree<T>> toTop ( Boolean includeSelf ) {
            List<Tree<T>> re = new LinkedList<> ();
            if ( includeSelf ) {
                  re.add ( this );
            }
            ReferTree<T> tmp = this;
            while ( tmp.hasUp () ) {
                  tmp = tmp.getUp ();
                  re.add ( tmp );
            }
            Collections.reverse ( re );
            return re;
      }

      /**
       *
       * @param includeSelf
       *
       * @return
       */
      public Collection<Map<Object , Object>> getList ( Boolean includeSelf ) {
            //向上转型，取消对象引用
            Collection<Map<Object , Object>> re = mapList;
            if (  ! includeSelf ) {
                  re.remove ( ROOT );
            }
            return re;
      }

      /**
       *
       * @return
       */
      public Collection<Map<Object , Object>> getList () {
            return getList ( false );
      }

      @Override
      public Iterator<Map<Object , Object>> iterator () {
            return getList ( true ).iterator ();
      }

}
